﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Drawing;
using System.Runtime.Serialization.Formatters.Binary;
using LexLib.LexemNameSpace;

namespace LexLib.LexerNameSpace
{
    class LexerTree
    {
        [Serializable]
        private class LexerTreeItem
        {
            public List<LexerTreeItem> ChildTrees;
            public char symbol;
            public ILexemType type;
            public bool isEnd;

            public LexerTreeItem(char ch, ILexemType lt)
            {
                symbol = ch;
                ChildTrees = new List<LexerTreeItem>();
                type = lt;
                isEnd = false;
            }

            public LexerTreeItem()
            {
                ChildTrees = new List<LexerTreeItem>();
            }

            /// <summary>
            /// Добавление нового потомка
            /// </summary>
            /// <param name="ch"></param>
            public void AddСhild(char ch, ILexemType lt)
            {
                ChildTrees.Add(new LexerTreeItem(ch, lt));
            }
        }


        LexerTreeItem curItem;//Используется для прохода по дереву извне
        LexerTreeItem topItem;//Содержит вершину дерева
        private string curWord;//Содержит текущее слово

        public LexerTree()
        {
            ReadFromFile();
        }

        /// <summary>
        /// Добавление слова в список ключевых слов 
        /// </summary>
        /// <param name="keyWord"></param>
        public void AddKeyWord(string keyWord, ILexemType lt)
        {
            bool exist = false;
            LexerTreeItem lex = topItem;
            for (int i = 0; i < keyWord.Length; i++)
            {
                foreach (LexerTreeItem child in lex.ChildTrees)
                {
                    if (child.symbol == keyWord[i])
                    {
                        lex = child;
                        exist = true;
                        break;
                    }
                }
                if (!exist)
                {
                    lex.AddСhild(keyWord[i], lt);
                    lex = (LexerTreeItem)lex.ChildTrees[lex.ChildTrees.Count - 1];
                }
                exist = false;
            }
            lex.isEnd = true;
            lex.type = lt;
            WriteToFile();
        }

        /// <summary>
        /// Удаление из списка ключевых слов данного слова
        /// </summary>
        /// <param name="keyWord"></param>
        public void DeleteKeyWord(string keyWord)
        {
            foreach (LexerTreeItem child in topItem.ChildTrees)
            {
                if (child.symbol == keyWord[0])
                {
                    DeleteKeyWordInner(child, keyWord, 0);
                    break;
                }
            }
            WriteToFile();
        }

        private bool DeleteKeyWordInner(LexerTreeItem tree, string str, int ind)
        {
            if ((ind == str.Length - 1) && (tree.isEnd))
            {
                if (tree.ChildTrees.Count == 0)
                {
                    return true;
                }
                else
                {
                    tree.isEnd = false;
                    return false;
                }
            }
            else
            {
                foreach (LexerTreeItem child in tree.ChildTrees)
                {
                    if ((ind < str.Length - 1) && (child.symbol == str[ind + 1]))
                    {
                        if (DeleteKeyWordInner(child, str, ind + 1))
                        {
                            tree.ChildTrees.Remove(child);
                        }
                        break;
                    }
                }
                return ((tree.ChildTrees.Count == 0) && (!tree.isEnd));
            }
        }

        /// <summary>
        /// Проверка на существование данной буквы на данном месте в одном из ключевых слов
        /// </summary>
        /// <param name="ch"></param>
        /// <returns></returns>
        public bool IsLetterExist(char ch)
        {
            foreach (LexerTreeItem child in curItem.ChildTrees)
            {
                if (child.symbol == ch)
                {
                    curWord += ch.ToString();
                    curItem = child;
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Делает дерево полным
        /// </summary>
        public void Dispose()
        {
            curItem = topItem;
            curWord = "";
            curItem.type = new OtherKeyWord(true, "{\\cf2\\cb1 "); //поменял!!!!
        }

        /// <summary>
        /// Проверка на конец ключевого слова
        /// </summary>
        public bool IsEnd()
        {
            return (curItem.isEnd);
        }

        /// <summary>
        /// Возвращает текущую лексему
        /// </summary>
        public string CurWord
        {
            get
            {
                return curWord;
            }
        }

        /// <summary>
        /// Возвращает тип текущей лексемы
        /// </summary>
        public ILexemType GetLexemType
        {
            get
            {
                if (curItem.type is OtherKeyWord)
                {
                    bool b = ((OtherKeyWord)(curItem.type)).IsVariable;
                    string cl = curItem.type.GetColor;
                    return new OtherKeyWord(b, cl);
                }
                else
                {
                    return (ILexemType)Activator.CreateInstance(curItem.type.GetType());
                }
            }
        }

        /// <summary>
        /// Чтение списка ключевых слов из файла
        /// </summary>
        private void ReadFromFile()
        {
            FileStream fs = new FileStream("KeyWords.tdf", FileMode.Open);
            BinaryFormatter br = new BinaryFormatter();
            try
            {
                topItem = (LexerTreeItem)br.Deserialize(fs);
            }
            catch (Exception e)
            {
                topItem = new LexerTreeItem();
            }
            topItem.type = new OtherKeyWord(true, "{\\cf1\\cb1 ");
            topItem.isEnd = false;
            fs.Close();
            Dispose();
        }

        /// <summary>
        /// Запись списка ключевых слов в файл
        /// </summary>
        private void WriteToFile()
        {
            FileStream fs = new FileStream("KeyWords.tdf", FileMode.OpenOrCreate);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, topItem);
            fs.Close();
        }

        private static Dictionary<string, ILexemType> strArr;

        public Dictionary<string, ILexemType> GetAllKeyWords()
        {
            strArr = new Dictionary<string, ILexemType>();
            GetAllKeyWordsInner(topItem, "");
            return strArr;
        }

        private static void GetAllKeyWordsInner(LexerTreeItem item, string word)
        {
            word += item.symbol;
            if (item.isEnd)
            {
                strArr.Add(word, item.type);
            }
            if (item.ChildTrees.Count != 0)
            {
                foreach (LexerTreeItem child in item.ChildTrees)
                {
                    GetAllKeyWordsInner(child, word);
                }
            }
        }

        public void SetColorization(string str, bool b)
        {
            LexerTreeItem item = topItem;
            for (int i = 0; i < str.Length; i++)
            {
                foreach (LexerTreeItem child in item.ChildTrees)
                {
                    if (child.symbol == str[i])
                    {
                        item = child;
                        break;
                    }
                }
            }
            ((OtherKeyWord)item.type).Colored = b;
            WriteToFile();
        }
    }
}
